﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml.Linq;
using Zen4SyncPOCO.POCO.Admin;
using Zen4SyncPOCO.POCO.Scenario;
using Zen4SyncPOCO.POCO.MergeSync;
using Zen4SyncPOCO.POCO.SQLServerExecution;
using Zen4SyncPOCO.POCO.SQLCEExecution;
using Zen4SyncPOCO.POCO.SQLCEBackup;
using Zen4SyncPOCO.POCO.MEF;
using Zen4SyncPOCO.POCO.Enum;
using Zen4SyncPOCO.POCO.Enum.Scenario;
using Zen4SyncPOCO.Enum.Admin;
using Zen4SyncPOCO.Enum.MergeSync;

namespace Zen4SyncPOCO.ClientTestSessionDescriptorHandler
{
    internal class ClientTestSessionDescriptorCreator
    {
        /// <summary>
        /// Generates an XML ClientTestSessionDescriptor for the given clientSession.
        /// </summary>
        /// <param name="clientSession">The source clientSession used to generate the XML ClientTestSessionDescriptor</param>
        /// <returns>An XML ClientTestSessionDescriptor generated from the given clientSession.</returns>
        internal XElement SerializeForClientTestSessionDescriptor(ClientSession clientSession)
        {
            XElement clientSessionXml = new XElement("clientTestSessionDescriptor",
                new XElement("clientSession", new XAttribute("id", clientSession.clientSession_id), new XAttribute("testServerId", clientSession.clientSession_testServerId), new XAttribute("testSessionId", clientSession.clientSession_testSessionId),
                    new XElement("enableLog", new XAttribute("value", clientSession.clientSession_enableLog)),
                    // The scheduled start date time comes in UTC format, so no need to change it.
                    new XElement("scheduledStartDateTime", clientSession.clientSession_scheduledStartDateTime),
                    SerializeForClientTestSessionDescriptor(clientSession.clientSession_testScenario, clientSession.clientSession_activityContexts)
                )
            );

            return clientSessionXml;
        }

        /// <summary>
        /// Serializes the given testScenario to an XML representation suitable for an insertion in a ClientTestSessionDescriptor.
        /// </summary>
        /// <returns>The testScenario XML representation.</returns>
        private XElement SerializeForClientTestSessionDescriptor(TestScenario testScenario, List<ActivityContext> activityContexts)
        {
            XElement testScenarioXml =
                new XElement("testScenario", new XAttribute("id", testScenario.testScenario_id));

            // Sort activities on the execution order
            foreach (Activity activity in testScenario.testScenario_activities.OrderBy(act => act.activity_executionOrder))
            {
                // Find the context corresponding to the current activity
                ActivityContext activityContext = activityContexts.Where(context => context.activityContext_activityId == activity.activity_id).SingleOrDefault();

                testScenarioXml.Add(SerializeForClientTestSessionDescriptor(activity, activityContext));
            }

            return testScenarioXml;
        }

        private XElement SerializeForClientTestSessionDescriptor(Activity activity, ActivityContext activityContext)
        {
            XElement activityItemXml = null;
            XElement contextItemXml = null;

            // Activity node and activityContext node
            switch (activity.activity_activityTypeEnum)
            {
                case ActivityTypeEnum.SyncSession:
                    activityItemXml = SerializeForClientTestSessionDescriptor(activity.activity_syncSession);
                    contextItemXml = SerializeForClientTestSessionDescriptor(activityContext.activityContext_syncSessionContext);
                    break;
                case ActivityTypeEnum.SqlServerExecution:
                    activityItemXml = SerializeForClientTestSessionDescriptor(activity.activity_sqlServerExecution);
                    contextItemXml = SerializeForClientTestSessionDescriptor(activityContext.activityContext_sqlServerExecutionContext);
                    break;
                case ActivityTypeEnum.SqlServerCheckData:
                    activityItemXml = SerializeForClientTestSessionDescriptor(activity.activity_sqlServerCheckData);
                    contextItemXml = SerializeForClientTestSessionDescriptor(activityContext.activityContext_sqlServerCheckDataContext);
                    break;
                case ActivityTypeEnum.SqlCeExecution:
                    activityItemXml = SerializeForClientTestSessionDescriptor(activity.activity_sqlCeExecution);
                    contextItemXml = SerializeForClientTestSessionDescriptor(activityContext.activityContext_sqlCeExecutionContext);
                    break;
                case ActivityTypeEnum.SqlCeCheckData:
                    activityItemXml = SerializeForClientTestSessionDescriptor(activity.activity_sqlCeCheckData);
                    contextItemXml = SerializeForClientTestSessionDescriptor(activityContext.activityContext_sqlCeCheckDataContext);
                    break;
                case ActivityTypeEnum.SqlCeBackup:
                    activityItemXml = SerializeForClientTestSessionDescriptor(activity.activity_sqlCeBackup);
                    contextItemXml = SerializeForClientTestSessionDescriptor(activityContext.activityContext_sqlCeBackupContext);
                    break;
                case ActivityTypeEnum.MefExecution:
                    activityItemXml = SerializeForClientTestSessionDescriptor(activity.activity_mefExecution);
                    contextItemXml = SerializeForClientTestSessionDescriptor(activityContext.activityContext_mefExecutionContext);
                    break;
                default:
                    break;
            }

            // Overriding data node if we have overriding data
            XElement overridingDataXml = null;
            if (!String.IsNullOrEmpty(activityContext.activityContext_overridingData))
            {
                overridingDataXml =
                    new XElement("overridingData",
                        XElement.Parse(activityContext.activityContext_overridingData)
                    );
            }

            // Putting it all together
            XElement scenarioItemXml =
                new XElement("activity", new XAttribute("id", activity.activity_id),
                    new XElement("executionOrder", activity.activity_executionOrder),
                    new XElement("preWaitingTimeInSeconds", activity.activity_preWaitingTimeInSeconds),
                    new XElement("postWaitingTimeInSeconds", activity.activity_postWaitingTimeInSeconds),
                    new XElement("activityItem", new XAttribute("type", System.Enum.GetName(typeof(ActivityTypeEnum), activity.activity_activityTypeEnum)),
                        activityItemXml
                    ),
                    new XElement("activityContext", new XAttribute("id", activityContext.activityContext_id),
                        contextItemXml,
                        overridingDataXml
                    )
                );

            return scenarioItemXml;
        }


        #region Activities

        private XElement SerializeForClientTestSessionDescriptor(MefExecution mefExecution)
        {
            
            XElement activityXml =
                new XElement("mefExecution", new XAttribute("id", mefExecution.mefExecution_id),
                    new XElement("mefComponentDirectory", mefExecution.mefExecution_mefComponentDirectory),
                    new XElement("metadata", XElement.Parse(mefExecution.mefExecution_metadata))
                );

            return activityXml;
        }

        private XElement SerializeForClientTestSessionDescriptor(SqlceBackup sqlceBackup)
        {
            XElement activityXml =
                new XElement("sqlCeBackup", new XAttribute("id", sqlceBackup.sqlceBackup_id));

            return activityXml;
        }

        private XElement SerializeForClientTestSessionDescriptor(SqlceCheckData sqlceCheckData)
        {
            XElement activityXml =
                new XElement("sqlCeCheckData", new XAttribute("id", sqlceCheckData.sqlceCheckData_id),
                    new XElement("sqlQuery", CDataValue(sqlceCheckData.sqlceCheckData_sqlQuery)),
                    new XElement("expectedValue", new XAttribute("type", System.Enum.GetName(typeof(SqlValueTypeEnum), sqlceCheckData.sqlceCheckData_sqlValueTypeEnum)),
                        CDataValue(Encoding.UTF8.GetString(sqlceCheckData.sqlceCheckData_expectedValue))
                    )
                );

            return activityXml;
        }

        private XElement SerializeForClientTestSessionDescriptor(SqlceExecution sqlceExecution)
        {
            XElement activityXml = new XElement("sqlCeExecution", new XAttribute("id", sqlceExecution.sqlceExecution_id),
                new XElement("sqlQuery", CDataValue(sqlceExecution.sqlceExecution_sqlQuery))
            );

            return activityXml;
        }

        private XElement SerializeForClientTestSessionDescriptor(SqlServerCheckData sqlServerCheckData)
        {
            XElement activityXml =
                new XElement("sqlServerCheckData", new XAttribute("id", sqlServerCheckData.sqlServerCheckData_id),
                    new XElement("sqlQuery", CDataValue(sqlServerCheckData.sqlServerCheckData_sqlQuery)),
                    new XElement("expectedValue", new XAttribute("type", System.Enum.GetName(typeof(SqlValueTypeEnum), sqlServerCheckData.sqlServerCheckData_sqlValueTypeEnum)),
                        CDataValue(Encoding.UTF8.GetString(sqlServerCheckData.sqlServerCheckData_expectedValue))
                    )
                );

            return activityXml;
        }

        private XElement SerializeForClientTestSessionDescriptor(SqlServerExecution sqlServerExecution)
        {
            XElement activityXml =
                new XElement("sqlServerExecution", new XAttribute("id", sqlServerExecution.sqlServerExecution_id),
                    new XElement("sqlQuery", CDataValue(sqlServerExecution.sqlServerExecution_sqlQuery))
            );

            return activityXml;
        }


        private XElement SerializeForClientTestSessionDescriptor(SyncSession syncSessionActivity)
        {
            XElement syncSessionXml =
                new XElement("syncSession", new XAttribute("id", syncSessionActivity.syncSession_id), new XAttribute("exchangeType", System.Enum.GetName(typeof(ExchangeTypeEnum), syncSessionActivity.syncSession_exchangeTypeEnum)), new XAttribute("snapshotTransferType", System.Enum.GetName(typeof(SnapshotTransfertTypeEnum), syncSessionActivity.syncSession_snapshotTransferTypeEnum)),
                    new XElement("compressionLevel", syncSessionActivity.syncSession_compressionLevel),
                    new XElement("connectionRetryTimeout", syncSessionActivity.syncSession_connectionRetryTimeout),
                    new XElement("connectionTimeout", syncSessionActivity.syncSession_connectTimeout),
                    new XElement("hostName", syncSessionActivity.syncSession_hostName),
                    new XElement("loginTimetout", syncSessionActivity.syncSession_loginTimeout),
                    new XElement("postSyncCleanup", syncSessionActivity.syncSession_postSyncCleanup),
                    new XElement("publication", syncSessionActivity.syncSession_publication),
                    new XElement("queryTimeout", syncSessionActivity.syncSession_queryTimeout),
                    new XElement("receiveTimeout", syncSessionActivity.syncSession_receiveTimeout),
                    new XElement("sendTimeout", syncSessionActivity.syncSession_sendTimeout),

                    new XElement("publisher",
                            new XAttribute("id", syncSessionActivity.syncSession_publisher.publisher_id),
                            new XAttribute("networkType", System.Enum.GetName(typeof(NetworkTypeEnum), syncSessionActivity.syncSession_publisher.publisher_networkTypeEnum)),
                            new XAttribute("securityType", System.Enum.GetName(typeof(SecurityTypeEnum), syncSessionActivity.syncSession_publisher.publisher_securityTypeEnum)),
                        new XElement("name", syncSessionActivity.syncSession_publisher.publisher_name),
                        new XElement("address", syncSessionActivity.syncSession_publisher.publisher_address),
                        new XElement("database", syncSessionActivity.syncSession_publisher.publisher_database),
                        new XElement("login", CDataValue(syncSessionActivity.syncSession_publisher.publisher_login)),
                        new XElement("password", CDataValue(syncSessionActivity.syncSession_publisher.publisher_password))
                    ),

                    new XElement("distributor",
                            new XAttribute("id", syncSessionActivity.syncSession_distributor.distributor_id),
                            new XAttribute("networkType", System.Enum.GetName(typeof(NetworkTypeEnum), syncSessionActivity.syncSession_distributor.distributor_networkTypeEnum)),
                            new XAttribute("securityType", System.Enum.GetName(typeof(SecurityTypeEnum), syncSessionActivity.syncSession_distributor.distributor_securityTypeEnum)),
                        new XElement("name", syncSessionActivity.syncSession_distributor.distributor_name),
                        new XElement("address", syncSessionActivity.syncSession_distributor.distributor_address),
                        new XElement("login", CDataValue(syncSessionActivity.syncSession_distributor.distributor_login)),
                        new XElement("password", CDataValue(syncSessionActivity.syncSession_distributor.distributor_password)),
                        new XElement("profileName", syncSessionActivity.syncSession_distributor.distributor_profileName)
                    ),

                    new XElement("internet", new XAttribute("id", syncSessionActivity.syncSession_internet.internet_id),
                        new XElement("name", syncSessionActivity.syncSession_internet.internet_name),
                        new XElement("url", syncSessionActivity.syncSession_internet.internet_url),
                        new XElement("login", CDataValue(syncSessionActivity.syncSession_internet.internet_login)),
                        new XElement("password", CDataValue(syncSessionActivity.syncSession_internet.internet_password)),
                        new XElement("proxyServer", syncSessionActivity.syncSession_internet.internet_proxyServer),
                        new XElement("proxyLogin", CDataValue(syncSessionActivity.syncSession_internet.internet_proxyLogin)),
                        new XElement("proxyPassword", CDataValue(syncSessionActivity.syncSession_internet.internet_proxyPassword))
                    )
                );

            return syncSessionXml;
        }

        #endregion

        #region Activity Contexts

        private XElement SerializeForClientTestSessionDescriptor(SyncSessionContext syncSessionContext)
        {

            XElement contextXml =
                new XElement("syncSessionContext", new XAttribute("id", syncSessionContext.syncSessionContext_id),
                    new XElement("subscriber", new XAttribute("id", syncSessionContext.syncSessionContext_subscriber.subscriber_id), new XAttribute("validateType", System.Enum.GetName(typeof(ValidateTypeEnum), syncSessionContext.syncSessionContext_subscriber.subscriber_validateTypeEnum)),
                        new XElement("name", syncSessionContext.syncSessionContext_subscriber.subscriber_name),
                        new XElement("hostName", syncSessionContext.syncSessionContext_subscriber.subscriber_hostName),
                        new XElement("connectionString", syncSessionContext.syncSessionContext_subscriber.subscriber_connectionString),
                        new XElement("deleteSdfIfItExists", new XAttribute("value", syncSessionContext.syncSessionContext_subscriber.subscriber_deleteSdfIfItExists)),
                        new XElement("sdfFileName", syncSessionContext.syncSessionContext_subscriber.subscriber_sdfFileName),
                        new XElement("sdfFileDirectory", syncSessionContext.syncSessionContext_subscriber.subscriber_sdfFileDirectory),
                        new XElement("reinitializeSubscription", new XAttribute("value", syncSessionContext.syncSessionContext_subscriber.subscriber_reinitializeSubscription)),
                        new XElement("uploadBeforeReinitialize", new XAttribute("value", syncSessionContext.syncSessionContext_subscriber.subscriber_uploadBeforeReinitialize))
                    )
                );
            return contextXml;
        }

        private XElement SerializeForClientTestSessionDescriptor(MefExecutionContext mefExecutionContext)
        {
            XElement contextXml =
                new XElement("mefExecutionContext", new XAttribute("id", mefExecutionContext.mefExecutionContext_id),
                    new XElement("metadata", XElement.Parse(mefExecutionContext.mefExecutionContext_metadata))
                );

            return contextXml;
        }

        private XElement SerializeForClientTestSessionDescriptor(SqlceBackupContext sqlceBackupContext)
        {
            XElement contextXml =
                new XElement("sqlCeBackupContext", new XAttribute("id", sqlceBackupContext.sqlceBackupContext_id),
                    new XElement("sdfFileName", sqlceBackupContext.sqlceBackupContext_sdfFileName),
                    new XElement("sdfFileDirectory", sqlceBackupContext.sqlceBackupContext_sdfFileDirectory)
                );

            return contextXml;
        }


        private XElement SerializeForClientTestSessionDescriptor(SqlceCheckDataContext sqlceCheckDataContext)
        {
            // SQL query parameters
            XElement sqlQueryParametersXml = new XElement("sqlQueryParameters", new XAttribute("count", sqlceCheckDataContext.sqlceCheckDataContext_parameters.Count));

            foreach (var sqlParameter in sqlceCheckDataContext.sqlceCheckDataContext_parameters)
            {
                XElement parameterXml =
                    new XElement("parameter", new XAttribute("name", sqlParameter.sqlceCheckDataParameter_name), new XAttribute("type", System.Enum.GetName(typeof(SqlValueTypeEnum), sqlParameter.sqlceCheckDataParameter_sqlValueTypeEnum)),
                        CDataValue(Encoding.UTF8.GetString(sqlParameter.sqlceCheckDataParameter_value))
                    );

                sqlQueryParametersXml.Add(parameterXml);
            }

            // Context
            XElement contextXml =
                new XElement("sqlCeCheckDataContext", new XAttribute("id", sqlceCheckDataContext.sqlceCheckDataContext_id),
                    new XElement("connectionString", sqlceCheckDataContext.sqlceCheckDataContext_connectionString),
                    sqlQueryParametersXml
                );

            return contextXml;
        }

        private XElement SerializeForClientTestSessionDescriptor(SqlceExecutionContext sqlceExecutionContext)
        {
            // SQL query parameters
            XElement sqlQueryParametersXml = new XElement("sqlQueryParameters", new XAttribute("count", sqlceExecutionContext.sqlceExecutionContext_parameters.Count));

            foreach (var sqlParameter in sqlceExecutionContext.sqlceExecutionContext_parameters)
            {
                XElement parameterXml =
                    new XElement("parameter", new XAttribute("name", sqlParameter.sqlceExecutionParameter_name), new XAttribute("type", System.Enum.GetName(typeof(SqlValueTypeEnum), sqlParameter.sqlceExecutionParameter_sqlValueTypeEnum)),
                        CDataValue(Encoding.UTF8.GetString(sqlParameter.sqlceExecutionParameter_value))
                    );

                sqlQueryParametersXml.Add(parameterXml);
            }

            XElement contextXml =
                new XElement("sqlCeExecutionContext", new XAttribute("id", sqlceExecutionContext.sqlceExecutionContext_id),
                    new XElement("connectionString", sqlceExecutionContext.sqlCeExecutionContext_connectionString),
                    sqlQueryParametersXml
                );

            return contextXml;
        }

        private XElement SerializeForClientTestSessionDescriptor(SqlServerCheckDataContext sqlServerCheckDataContext)
        {
            // SQL query parameters
            XElement sqlServerCheckDataParametersXml = new XElement("sqlQueryParameters", new XAttribute("count", sqlServerCheckDataContext.sqlServerCheckDataContext_parameters.Count));

            foreach (var sqlParameter in sqlServerCheckDataContext.sqlServerCheckDataContext_parameters)
            {
                XElement parameterXml =
                    new XElement("parameter", new XAttribute("name", sqlParameter.sqlServerCheckDataParameter_name), new XAttribute("type", System.Enum.GetName(typeof(SqlValueTypeEnum), sqlParameter.sqlServerCheckDataParameter_sqlValueTypeEnum)),
                        CDataValue(Encoding.UTF8.GetString(sqlParameter.sqlServerCheckDataParameter_value))
                    );
                
                sqlServerCheckDataParametersXml.Add(parameterXml);
            }

            // Context
            XElement contextXml =
                new XElement("sqlServerCheckDataContext", new XAttribute("id", sqlServerCheckDataContext.sqlServerCheckDataContext_id),
                    new XElement("connectionString", sqlServerCheckDataContext.sqlServerCheckDataContext_connectionString),
                    sqlServerCheckDataParametersXml
                );

            return contextXml;
        }

        private XElement SerializeForClientTestSessionDescriptor(SqlServerExecutionContext sqlServerExecutionContext)
        {
            // SQL query parameters
            XElement sqlQueryParametersXml = new XElement("sqlQueryParameters", new XAttribute("count", sqlServerExecutionContext.sqlServerExecutionContext_parameters.Count));
            foreach (var sqlParameter in sqlServerExecutionContext.sqlServerExecutionContext_parameters)
            {
                sqlQueryParametersXml.Add(
                    new XElement("parameter", new XAttribute("name", sqlParameter.sqlServerExecutionParameter_name), new XAttribute("type", System.Enum.GetName(typeof(SqlValueTypeEnum), sqlParameter.sqlServerExecutionParameter_sqlValueTypeEnum)),
                        CDataValue(Encoding.UTF8.GetString(sqlParameter.sqlServerExecutionParameter_value))
                    )
                );
            }

            XElement contextXml =
                new XElement("sqlServerExecutionContext", new XAttribute("id", sqlServerExecutionContext.sqlServerExecutionContext_id),
                    new XElement("connectionString", sqlServerExecutionContext.sqlServerExecutionContext_connectionString),
                    sqlQueryParametersXml
            );

            return contextXml;
        }

        #endregion


        /// <summary>
        /// Encapsulates the given value in a CDATA section and returns the CDATA section.
        /// This method is useful because the CDATA constructor does not like null values.
        /// If the given value is null, this method simply returns null.
        /// </summary>
        /// <param name="value"></param>
        /// <returns>A CDATA section encapsulating the given value if the given value is not null, null otherwise.</returns>
        private XCData CDataValue(string value)
        {
            if (String.IsNullOrEmpty(value))
                return null;
            else
                return new XCData(value);
        }
    }
}
